#include "Server.h"

string tag = "[Server] ";
int childcount = 0;

Server::Server():log(FuncionesComunes::GetNombreLog()),socket(FuncionesComunes::Puerto) {
	clientesSinConv.clear();
	conversaciones.clear();
	canalServidor = new Fifo (FuncionesComunes::GetFifoServidor().c_str());
	SignalHandler::getInstance()->registrarHandler(SIGINT, &sigint_handler);
}

/**
 * En este método se va a abrir un socket con un puerto determinado default.
 * En este cada uno de los clientes deberá escribir un mensaje según el tipo de operación que quiera
 * efectuar. El server se quedará en espera a todos los clientes.
 */
void Server::ejecutar(){
	Mensaje pedido;

	escribirLog("Conectando Servidor");
	escribirLog("Esperando conexiones");

	while(sigint_handler.getGracefulQuit() == 0){
		pedido = recibirPedido() ;
		Mensaje respuesta ( procesarPedido(pedido) );
		enviarRespuesta(respuesta);
		escribirLog("Respuesta enviada exitosamente");
	}
	cerrarConexion();
}

void Server::cerrarConexion(){
	canalServidor->cerrar();
	SignalHandler::getInstance()->destruir();
}

void Server::activarModoDebug(bool activar){
	log.activeDebug(activar);
}

void Server::escribirLog(string mensaje){
	log.escribir(tag + mensaje + "\n");
}

Mensaje Server::recibirPedido(){
	char buffer [ BUFFSIZE ];

	escribirLog("Esperando pedido");
	int bytesRecibidos = canalServidor->leer ( buffer,BUFFSIZE );
	escribirLog("Pedido recibido");
	buffer [ bytesRecibidos ] = '\0';
	Mensaje m (buffer);
	escribirLog("El pedido es: " + m.toString());

	return m;
}

Mensaje Server::procesarPedido(Mensaje pedido){
	Mensaje respuesta;
	string src = pedido.getSrc();
	int pid = pedido.getIdCliente();

	switch (pedido.getTipo()){
	case CONECTAR:
		escribirLog("Mensaje tipo: CONECTAR");
		respuesta = conectarCliente(pid,pedido.getSrc(), pedido.getMensaje());
		break;
	case DESCONECTAR:
		escribirLog("Mensaje tipo: DESCONECTAR");
		desconectarCliente(pid);
		break;
	case INICIAR_CONVERSACION:
		escribirLog("Mensaje tipo: INICIAR_CONVERSACION");
		respuesta = iniciarConv(pid);
		break;
	case CONV_ACTIVAS:
		escribirLog("Mensaje tipo: CONVERSACIONES_ACTIVAS");
		respuesta = listarConversarciones(pid);
		break;
	case PARTIC_ACTIVOS:
		escribirLog("Mensaje tipo: PARTICIPANTES_ACTIVOS");
		respuesta = listarClientes(pid,pedido.getIdConversacion());
		break;
	case UNIRSE_CONV:
		escribirLog("Mensaje tipo: UNIRSE_CONVERSACION");
		if(verificaNombre(pid,pedido.getIdConversacion())) {
			escribirLog("Nombre: "+ src +" verificado" );
			escribirLog("Uniendo al cliente: " + src+ " a la conversación " + FuncionesComunes::ConvertirAString(pedido.getIdConversacion()));
			InfoCliente info;
			info.apodo = src;
			info.pathFifo = pedido.getMensaje();
			conversaciones[pedido.getIdConversacion()].getParticipantes().insert(pair<int,InfoCliente>(atoi(src.c_str()),info));
			respuesta = mensajeUnion(src);
		}
		else respuesta = mensajeError( src, "Nombre inválido");
		break;
	case DEJAR_CONV:
		escribirLog("Mensaje tipo: DEJAR_CONVERSACION");
		abandonarConversacion(pedido.getIdCliente(),pedido.getIdConversacion());
		//TODO: Enviar mensaje de abandono de conversación, si es necesario
		break;
	case CONVERSAR:
		escribirLog("Mensaje tipo: CONVERSAR");
		break;
	default:
		escribirLog("Mensaje tipo desconocido");
		break;
	}

	return respuesta;
}

Mensaje Server::conectarCliente(int id, string cliente, string path){
	escribirLog("Conectando al cliente: " + cliente);
	InfoCliente info;
	info.apodo = cliente;
	info.pathFifo = path;
	clientesSinConv.insert(pair<int,InfoCliente>(id,info));
	return mensajeBienvenida(path);
}

Mensaje Server::mensajeUnion(string dest){
	Mensaje m(NOMBRE_SERVIDOR,dest,OK,"Se ha unido a la conversación");
	return m;
}

Mensaje Server::mensajeBienvenida(string dest){
	Mensaje m(NOMBRE_SERVIDOR,dest,OK,"Bienvenido a ConcuConversation");
	return m;
}

bool Server::hayClientes(){
	return (!clientesSinConv.empty() || conversaciones.size() > 0);
}

void Server::enviarRespuesta(Mensaje m){
	escribirLog("Enviando respuesta: " + m.getDest());
	//socket.enviar(FuncionesComunes::ConvertirACharPointer(m.toString()));
	Fifo canal (m.getDest().c_str());
	char buffer[BUFFSIZE];
	memcpy (buffer, m.toString().c_str(), strlen(m.toString().c_str()));
	int bytes = canal.escribir(buffer, BUFFSIZE);
	escribirLog("Escribi " + FuncionesComunes::ConvertirAString(bytes));
	canal.cerrar();
}

void Server::desconectarCliente(int id){
	map<int, InfoCliente>::iterator it = clientesSinConv.find(id);
	escribirLog("Desconectando cliente: " + it->second.apodo);
	clientesSinConv.erase(id);
}

Mensaje Server::abandonarConversacion(int cliente, int idConversacion){

	conversaciones[idConversacion].eliminarParticipante(cliente);
	if(conversaciones[idConversacion].getParticipantes().empty()) eliminarConv(idConversacion);
	//TODO: Armar el mensaje para comunicar que un miembro dejó la conversación.
	Mensaje m;
	return m;
}

bool Server::verificaNombre(int pid, int i){
	return (conversaciones[i].getParticipantes().find(pid) == conversaciones[i].getParticipantes().end());
}

Mensaje Server::iniciarConv(int pidFundador){
	Conversacion conv (conversaciones.size());

	map<int,InfoCliente>::iterator it = clientesSinConv.find(pidFundador);
	escribirLog("Iniciando conversación a pedido de: " +(*it).second.apodo);
	clientesSinConv.erase(pidFundador);
	conv.registrarParticipante(pidFundador,(*it).second);
	escribirLog("Registrando conversación.");
	conversaciones.push_back(conv);
	escribirLog("Armando respuesta conversacion OK");
	escribirLog("Cantidad de conversaciones = " + FuncionesComunes::ConvertirAString(conversaciones.size()));
	Mensaje m (NOMBRE_SERVIDOR, (*it).second.pathFifo, OK, "Conversacion iniciada");
	m.setIdConversacion(conv.getId());

	return m;
}

void Server::eliminarConv(int id){
	escribirLog("Eliminando conversación por falta de participantes" );
	conversaciones[id].setActiva(false);
	conversaciones[id].getParticipantes().clear();
	escribirLog("Conversacion " + FuncionesComunes::ConvertirAString(id) + " eliminada" );
}

Mensaje Server::mensajeError(string dest, string mensaje ){
	Mensaje m(NOMBRE_SERVIDOR, dest,ERROR, mensaje);
	escribirLog("Escribiendo mensaje de error: " + mensaje + " a " + dest);

	return m;
}

Mensaje Server::listarConversarciones(int pid){
	escribirLog("Listando conversaciones");
	string contenido="";

	if(!conversaciones.empty()){
		vector<Conversacion>::iterator it = conversaciones.begin();
		if( (*it).getActiva() ){
			string buffer = "id conversacion: " + FuncionesComunes::ConvertirAString((*it).getId()) + " nombre conversacion: "+ (*it).getNombre() + FuncionesComunes::Separador;
			contenido += buffer;
		}
		it++;
	}

	map<int,InfoCliente>::iterator it = clientesSinConv.find(pid);
	Mensaje m (NOMBRE_SERVIDOR, it->second.pathFifo, CONV_ACTIVAS, contenido);
	m.setIdCliente(pid);
	return m;
}

Mensaje Server::listarClientes(int pid ,int idConversacion){
	escribirLog("Listando clientes");
	string contenido="";
	int tam = conversaciones.size();
	if(idConversacion < tam){
		if(conversaciones[idConversacion].getActiva()){
		map<int,InfoCliente>::iterator it = conversaciones[idConversacion].getParticipantes().begin();
		contenido += it->second.apodo;
		it++;
		}
	}
	map<int,InfoCliente>::iterator it = clientesSinConv.find(pid);
	Mensaje m(NOMBRE_SERVIDOR, it->second.pathFifo,PARTIC_ACTIVOS,contenido);
	m.setIdCliente(pid);
	return m;
}


Server::~Server() {
	delete canalServidor;
}
